package mongo

import (
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"gogame/gameconfig"
	"gogame/logger"
	"reflect"
)

type MongoStruct struct {
	Options  gameconfig.MongoDb // mongo配置
	Client   *mongo.Client      // mongo client
	DataBase *mongo.Database    // mongo database
}

func NewMongoStruct(ver string) *MongoStruct {
	return &MongoStruct{
		Options: InitOptions(ver),
	}
}

var Mdb *MongoStruct
var CrossMdb *MongoStruct

// InitMongo 初始化mongodb
func initMongo(ver string) *MongoStruct {
	switch ver {
	case "cross":
		if CrossMdb != nil {
			return CrossMdb
		}
	default:
		if Mdb != nil {
			return Mdb
		}
	}

	_mdb := NewMongoStruct(ver)

	_optionsClient := options.Client()
	// 设置mongo地址
	_optionsClient.ApplyURI(_mdb.Options.Url)
	// 设置连接池大小
	_optionsClient.SetMaxPoolSize(_mdb.Options.PoolSize)

	_client, err := mongo.Connect(context.Background(), _optionsClient)
	if err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongo connect fail!\n err: %s", err))
		return nil
	}
	_mdb.Client = _client
	_mdb.DataBase = _client.Database(_mdb.Options.DbName)

	switch ver {
	case "cross":
		CrossMdb = _mdb
	default:
		Mdb = _mdb
	}

	return _mdb
}

// InitMongo 初始化mongodb
func InitMongo() *MongoStruct {
	return initMongo("")
}

// InitCrossMongo 初始化跨服mongodb
func InitCrossMongo() *MongoStruct {
	return initMongo("cross")
}

// GetTableList 获取数据库所有表
func (mdb *MongoStruct) GetTableList() []string {
	_tableList, _ := mdb.DataBase.ListCollectionNames(context.Background(), bson.D{})

	return _tableList
}

// Count 统计
func (mdb *MongoStruct) Count(tableName string, filter bson.M, opts ...*options.CountOptions) int64 {
	_num, _err := mdb.DataBase.Collection(tableName).CountDocuments(context.Background(), filter, opts...)
	if _err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb Count fail!\nerr-->%s", _err))
		return 0
	}
	return _num
}

// FindOne 查询单条数据
func (mdb *MongoStruct) FindOne(tableName string, filter bson.M, opts ...*options.FindOneOptions) *mongo.SingleResult {
	res := mdb.DataBase.Collection(tableName).FindOne(context.Background(), filter, opts...)
	return res

}

// FindOneByReflect 查询单条数据 反射处理
func (mdb *MongoStruct) FindOneByReflect(tableName string, filter bson.M, val any, opts ...*options.FindOneOptions) error {
	res := mdb.DataBase.Collection(tableName).FindOne(context.Background(), filter, opts...)

	_err := res.Decode(reflect.ValueOf(val).Interface())
	if _err != nil {
		if _err != mongo.ErrNoDocuments {
			logger.WorkerLog.Warn(fmt.Sprintf("mongodb FindOneByReflect fail!\n tableName:%s filter:%v \nerr->%s", tableName, filter, _err))
		}
	}

	return _err

}

// Find 查询多条数据
func (mdb *MongoStruct) Find(tableName string, filter bson.M, opts ...*options.FindOptions) *mongo.Cursor {
	_cur, _err := mdb.DataBase.Collection(tableName).Find(context.Background(), filter, opts...)
	if _err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb Find fail!\nerr-->%s", _err))
		return nil
	}

	return _cur
}

// FindByReflect 查询多条数据 反射处理
func (mdb *MongoStruct) FindByReflect(tableName string, filter bson.M, sliceVal any, opts ...*options.FindOptions) error {
	_cur, _err := mdb.DataBase.Collection(tableName).Find(context.Background(), filter, opts...)
	if _err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb FindByReflect fial!\nerr ->%s", _err))
		return _err
	}
	// 关闭游标对象
	defer _cur.Close(context.Background())

	destSlice := reflect.Indirect(reflect.ValueOf(sliceVal))
	destType := destSlice.Type().Elem().Elem()

	// 所有数据遍历出来
	for _cur.Next(context.Background()) {
		_oneVal := reflect.New(destType)
		err := _cur.Decode(_oneVal.Elem().Addr().Interface())
		if err != nil {
			logger.WorkerLog.Warn(fmt.Sprintf("mongodb Find fial!\nerr ->%s", _err))
			continue
		}
		destSlice.Set(reflect.Append(destSlice, _oneVal))
	}

	return nil
}

// FindByReflectToList 查询多条数据并返回结果list 反射处理
func (mdb *MongoStruct) FindByReflectToList(tableName string, filter bson.M, sliceVal any, opts ...*options.FindOptions) ([]any, error) {
	_cur, _err := mdb.DataBase.Collection(tableName).Find(context.Background(), filter, opts...)
	if _err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb FindByReflect fail\nerr ->%s", _err))
		return nil, _err
	}
	// 关闭游标对象
	defer _cur.Close(context.Background())

	destSlice := reflect.Indirect(reflect.ValueOf(sliceVal))
	destType := destSlice.Type().Elem().Elem()

	var anyList []any
	// 所有数据遍历出来
	for _cur.Next(context.Background()) {
		_oneVal := reflect.New(destType)
		err := _cur.Decode(_oneVal.Elem().Addr().Interface())
		if err != nil {
			logger.WorkerLog.Warn(fmt.Sprintf("mongodb Find fail!\nerr ->%s", _err))
			continue
		}
		destSlice.Set(reflect.Append(destSlice, _oneVal))
		anyList = append(anyList, _oneVal.Elem().Addr().Interface())
	}

	return anyList, nil
}

// Delete 删除数据
func (mdb *MongoStruct) Delete(tableName string, filter bson.M, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) {
	_res, _err := mdb.DataBase.Collection(tableName).DeleteMany(context.Background(), filter, opts...)
	if _err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb Delete fail\nerr ->%s", _err))
	}

	return _res, _err
}

// Update 更新数据
func (mdb *MongoStruct) Update(tableName string, filter bson.M, updateData any, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) {
	_res, _err := mdb.DataBase.Collection(tableName).UpdateMany(
		context.Background(),
		filter,
		bson.M{
			"$set": updateData,
		},
		opts...,
	)
	if _err != nil {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb Update fail!\nerr ->%s", _err))
	}

	return _res, _err
}

// InsertMany 插入多条数据
func (mdb *MongoStruct) InsertMany(tableName string, insertList []any, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) {
	var err error

	if len(insertList) == 0 {
		err = fmt.Errorf("mongodb InsertMany data.Len == 0")
		logger.WorkerLog.Warn(err.Error())
		return nil, err
	}

	_res, err := mdb.DataBase.Collection(tableName).InsertMany(
		context.Background(),
		insertList,
		opts...,
	)
	if err != nil && !isModelLog(tableName) {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb InsertMany fail!\nerr ->%s", err))
	}

	return _res, err
}

// InsertOne 插入单挑数据
func (mdb *MongoStruct) InsertOne(tableName string, insertList any, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) {
	_res, err := mdb.DataBase.Collection(tableName).InsertOne(
		context.Background(),
		insertList,
		opts...,
	)
	if err != nil && !isModelLog(tableName) {
		logger.WorkerLog.Warn(fmt.Sprintf("mongodb InsertOne fail!\nerr ->%s", err))
	}

	return _res, err
}

func isModelLog(tableName string) bool {
	if tableName == "model_log" {
		return true
	}
	return false
}
